﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Drawing.Design;
using System.Runtime.Serialization;
using System.Workflow.Activities;
using System.Workflow.Runtime;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using OpenComposite.Workflow.Properties;

namespace OpenComposite.Workflow
{
	#region EventBasesExclusiveOr
	[Designer(typeof(EventBasedXOrDesigner))]
	[ToolboxBitmap(typeof(resfinder), "OpenComposite.Workflow.Resources.eventbased.PNG")]
	[ToolboxItem(typeof(EventBasedXOrActivityToolboxItem))]
	public class EventBasedXOrActivity : DecisionActivity, IActivityEventListener<QueueEventArgs>
	{
		public static readonly DependencyProperty EventHandlingTypeProperty = DependencyProperty.Register("EventHandlingType", typeof(EventHandlingType), typeof(EventBasedXOrActivity), new PropertyMetadata((object)EventHandlingType.Scope));

		[DefaultValue(EventHandlingType.Scope)]
		public EventHandlingType EventHandlingType
		{
			get { return (EventHandlingType)GetValue(EventHandlingTypeProperty); }
			set { SetValue(EventHandlingTypeProperty, value); }
		}

		protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
		{
			foreach ( EventDrivenBranch branch in this.EnabledActivities ) {
				if ( ( (IncomingEvent)branch.Activities[0] ).EventType == EventType.Termination ) {
					branch.Closed += Terminated;
				} else {
					branch.Closed += new EventHandler<ActivityExecutionStatusChangedEventArgs>(branchClosed);
				}
				executionContext.ExecuteActivity(branch);
			}
			return ActivityExecutionStatus.Executing;
		}

		void branchClosed(object sender, ActivityExecutionStatusChangedEventArgs e)
		{
			if ( EventHandlingType == EventHandlingType.Scope ) {
				ActivityExecutionContext context = sender as ActivityExecutionContext;
				if ( e.ExecutionResult == ActivityExecutionResult.Canceled ) {
					if ( context.Activity.ExecutionStatus == ActivityExecutionStatus.Faulting ) {
						WorkflowHelpers.CompleteCloseContext(context);
					} else {
						branchFinished(sender, e);
					}
					return;
				}
				ActivityExecutionContext executeContext = context.ExecutionContextManager.CreateExecutionContext(e.Activity);
				executeContext.Activity.Closed += new EventHandler<ActivityExecutionStatusChangedEventArgs>(branchClosed);
				executeContext.ExecuteActivity(executeContext.Activity);
			}
		}

		void branchFinished(object sender, ActivityExecutionStatusChangedEventArgs e)
		{
			ActivityExecutionContext context = sender as ActivityExecutionContext;
			ClearContext(context);

			if ( Cancel(context) == ActivityExecutionStatus.Closed ) {
				WorkflowHelpers.SecureCloseContext(context);
			}
		}

		protected override ActivityExecutionStatus HandleFault(ActivityExecutionContext executionContext, Exception exception)
		{
			return base.HandleFault(executionContext, exception);
		}

		protected override ActivityExecutionStatus Cancel(ActivityExecutionContext executionContext)
		{
			bool isOkToClose = true;
			for ( int i = 0; i < base.EnabledActivities.Count; i++ ) {
				EventDrivenBranch activity = base.EnabledActivities[i] as EventDrivenBranch;
				ActivityExecutionContext context = executionContext.ExecutionContextManager.GetExecutionContext(activity);
				if ( context != null ) {
					switch ( context.Activity.ExecutionStatus ) {
						case ActivityExecutionStatus.Executing:
							context.Activity.Closed += branchFinished;
							context.CancelActivity(context.Activity);
							isOkToClose = false;
							break;

						case ActivityExecutionStatus.Canceling:
						case ActivityExecutionStatus.Faulting:
							isOkToClose = false;
							break;
					}
				}
			}
			if ( isOkToClose ) {
				return ActivityExecutionStatus.Closed;
			}

			return ActivityExecutionStatus.Canceling;
		}

		void Terminated(object sender, ActivityExecutionStatusChangedEventArgs e)
		{
			//e.Activity.Closed -= this.Terminated;
			ActivityExecutionContext context = sender as ActivityExecutionContext;
			foreach ( EventDrivenBranch branch in this.EnabledActivities ) {
				if ( branch.ExecutionStatus == ActivityExecutionStatus.Executing ) {
					branch.Closed -= this.branchClosed;
					branch.Closed += new EventHandler<ActivityExecutionStatusChangedEventArgs>(branchFinished);
					context.CancelActivity(branch);
				}
			}

			//foreach ( ActivityExecutionContext ctx in context.ExecutionContextManager.ExecutionContexts ) {
			//    if ( ctx.Activity.ExecutionStatus == ActivityExecutionStatus.Executing ) {
			//        ctx.CancelActivity(ctx.Activity);
			//    }
			//}
			ClearContext(context);

			if ( Cancel(context) == ActivityExecutionStatus.Closed &&
				 context.Activity.ExecutionStatus == ActivityExecutionStatus.Closed ) {
				WorkflowHelpers.SecureCloseContext(context);
			}
		}

		private void ClearContext(ActivityExecutionContext context)
		{
			for ( int i = context.ExecutionContextManager.ExecutionContexts.Count; i > 0; i-- ) {
				ActivityExecutionContext ctx = context.ExecutionContextManager.ExecutionContexts[i - 1];
				if ( ctx.Activity.ExecutionStatus == ActivityExecutionStatus.Executing ) {
					ctx.CancelActivity(ctx.Activity);
				}
				if ( ctx.Activity.ExecutionStatus == ActivityExecutionStatus.Closed ) {
					context.ExecutionContextManager.CompleteExecutionContext(ctx);
				}
			}
		}

		protected override void OnClosed(IServiceProvider provider)
		{
			base.OnClosed(provider);
		}

		#region IActivityEventListener<QueueEventArgs> Members

		public void OnEvent(object sender, QueueEventArgs e)
		{

		}

		#endregion
	}

	public class EventBasedXOrDesigner : DecisionDesigner
	{
		private Pen innerSymbolPenM = new Pen(Brushes.Black, 2.5F);

		protected override LinearGradientBrush BackgroundBrush
		{
			get
			{
				Point start = new Point(bounds.Top + Bounds.Height / 2, bounds.Top);
				Point end = new Point(bounds.Top + Bounds.Height / 2, bounds.Bottom);
				return new LinearGradientBrush(start, end, Color.White, Color.DarkRed);
			}
		}
		protected override SolidBrush JoinBrush
		{
			get { return (SolidBrush)Brushes.DarkRed; }
		}
		protected override Pen ConnectorPen
		{
			get { return new Pen(Color.DarkRed); }
		}

		protected override void OnPaint(ActivityDesignerPaintEventArgs e)
		{
			base.OnPaint(e);
		}

		#region Paint Methods
		protected override void PaintSplit(ActivityDesignerPaintEventArgs e)
		{
			base.PaintSplit(e);

			bounds.Inflate(-8, -8);

			e.Graphics.DrawEllipse(e.DesignerTheme.ForegroundPen, bounds);
			bounds.Inflate(-2, -2);
			e.Graphics.DrawEllipse(e.DesignerTheme.ForegroundPen, bounds);
			bounds.Inflate(-3, -3);
			e.Graphics.FillPolygon(e.DesignerTheme.ForegroundBrush,
				new Point[]{
					new Point(bounds.Left+bounds.Width/2,bounds.Top),
					new Point(bounds.Left+1,bounds.Bottom-4),
					new Point(bounds.Right-1,bounds.Bottom-4)
				});
			e.Graphics.FillPolygon(e.DesignerTheme.ForegroundBrush,
				new Point[]{
					new Point(bounds.Left+1,bounds.Top+4),
					new Point(bounds.Left+bounds.Width/2,bounds.Bottom),
					new Point(bounds.Right-1,bounds.Top+4)
				});
		}

		protected override void PaintJoin(ActivityDesignerPaintEventArgs e)
		{
			base.PaintJoin(e);

			bounds.Inflate(-13, -13);

			int x1 = bounds.Left;
			int x2 = bounds.Right;
			int y1 = bounds.Top;
			int y2 = bounds.Bottom;
			e.Graphics.DrawLine(innerSymbolPenM, x1, y1, x2, y2); // draw horizontal line
			e.Graphics.DrawLine(innerSymbolPenM, x2, y1, x1, y2); // draw vertical line
		}
		#endregion

		//protected override int SplitInflate
		//{
		//    get { return -13; }
		//}

		//protected override int JoinInflate
		//{
		//    get { return 13; }
		//}

		protected override BranchType BranchType
		{
			get { return BranchType.Event; }
		}

	}
	#endregion

	#region IncomingEvent (IEventActivity)
	[Designer(typeof(IncomingEventDesigner))]
	public class IncomingEvent : LogicalInputActivity
	{
		public static DependencyProperty EventTypeProperty = DependencyProperty.Register("EventType", typeof(EventType), typeof(IncomingEvent));
		public EventType EventType
		{
			get { return (EventType)GetValue(EventTypeProperty); }
			set { SetValue(EventTypeProperty, value); }
		}

		public static DependencyProperty ProbabilityProperty = DependencyProperty.Register("Probability", typeof(double), typeof(IncomingEvent));
		public double Probability
		{
			get { return (double)GetValue(ProbabilityProperty); }
			set
			{
				if ( value > 100 )
					value = 100;
				else if ( value < 0.0 )
					value = 0;

				SetValue(ProbabilityProperty, value);
			}
		}

		public IEventActivity EventActivity
		{
			get { return this as IEventActivity; }
		}

		protected override void OnInvoked(EventArgs e)
		{
			base.OnInvoked(e);
		}

	}

	public class IncomingEventDesigner : ActivityDesigner
	{
		private const int _sizeVal = 30;
		private readonly Size _size = new Size(_sizeVal, _sizeVal);
		private Rectangle bounds;

		protected override void OnDragDrop(ActivityDragEventArgs e)
		{
			if ( e.Activities.Count != 1 ) return;

			if ( e.Activities[0] is LogicalInputActivity ) {
				LogicalInputActivity lia = e.Activities[0] as LogicalInputActivity;
				IncomingEvent ie = this.Activity as IncomingEvent;
				ie.Name = lia.Name;
				ie.Description = lia.Description;

				ie.LogicalActivityId = lia.LogicalActivityId;
				ie.LogicalActivityName = lia.LogicalActivityName;

				ie.EventName = lia.EventName;
				ie.InterfaceType = lia.InterfaceType;
				ie.CorrelationToken = lia.CorrelationToken;
				ie.Roles = lia.Roles;
				ie.Text = lia.EventName;
				ie.EventType = ie.Text.ToLower() == "exit" ? EventType.Termination : EventType.Message;
			}
			base.OnDragDrop(e);
		}

		protected override void OnDragOver(ActivityDragEventArgs e)
		{
			if ( e.Activities.Count != 1 ) return;

			if ( e.Activities[0] is LogicalInputActivity ) {
				e.Effect = DragDropEffects.Move;
			}
			base.OnDragOver(e);
		}

		protected override void OnMouseDragMove(MouseEventArgs e)
		{
			base.OnMouseDragMove(e);
		}

		protected override void OnMouseDragEnd()
		{
			base.OnMouseDragEnd();
		}

		protected override void OnPaint(ActivityDesignerPaintEventArgs e)
		{
			if ( !IsVisible )
				return;

			Pen connectorPen = new Pen(Color.DarkRed);

			// draw basic shape
			bounds = Bounds;
			bounds.Inflate(-2, -2);
			e.Graphics.DrawEllipse(connectorPen, bounds);
			bounds.Inflate(-2, -2);
			e.Graphics.DrawEllipse(connectorPen, bounds);

			// draw inner shapes
			switch ( (EventType)Activity.GetValue(IncomingEvent.EventTypeProperty) ) {
				case EventType.None:
					break;
				case EventType.Message:
					bounds.Inflate(-3, -4);
					e.Graphics.DrawRectangle(e.DesignerTheme.ForegroundPen, bounds);
					e.Graphics.DrawPolygon(e.DesignerTheme.ForegroundPen,
						new Point[]{
							new Point(bounds.Left, bounds.Top),
							new Point(bounds.Left + bounds.Width / 2, bounds.Y + bounds.Height / 2),
							new Point(bounds.Right, bounds.Top)
						});
					break;
				case EventType.Timer:
					bounds.Inflate(-3, -3);
					List<string> fu = new List<string>();
					e.Graphics.DrawEllipse(e.DesignerTheme.ForegroundPen, bounds);
					e.Graphics.DrawLine(e.DesignerTheme.ForegroundPen,
						WorkflowHelpers.GetCenter(bounds),
						new Point(WorkflowHelpers.GetCenter(bounds).X + 4, WorkflowHelpers.GetCenter(bounds).Y - 4));
					e.Graphics.DrawLine(e.DesignerTheme.ForegroundPen,
						WorkflowHelpers.GetCenter(bounds),
						new Point(WorkflowHelpers.GetCenter(bounds).X + 3, WorkflowHelpers.GetCenter(bounds).Y + 3));
					break;
				case EventType.Rule:
					break;
				case EventType.Exception:
					bounds.Inflate(-7, -2);
					GraphicsPath path = new GraphicsPath();
					path.AddLine(bounds.Left, bounds.Top, bounds.Left, bounds.Bottom);
					path.AddLine(bounds.Left, bounds.Bottom, bounds.Left + 1, bounds.Top - 1);
					path.CloseFigure();
					path.AddLine(bounds.Right, bounds.Bottom, bounds.Right, bounds.Top);
					path.AddLine(bounds.Right, bounds.Top, bounds.Right - 1, bounds.Bottom + 1);
					path.CloseFigure();
					path.AddLine(bounds.Right - 1, bounds.Bottom, bounds.Left + 1, bounds.Top);
					e.Graphics.DrawPath(e.DesignerTheme.ForegroundPen, path);
					//graphics.FillPath(theme.ForegroundBrush, path);
					break;
				case EventType.Cancellation:
					break;
				case EventType.Compensation:
					bounds.Inflate(-3, -5);
					Point center = WorkflowHelpers.GetCenter(bounds);
					e.Graphics.FillPolygon(e.DesignerTheme.ForegroundBrush,
						new Point[]{
							new Point(bounds.Left, center.Y),
							new Point(center.X, bounds.Top),
							new Point(center.X, bounds.Bottom)
						});
					e.Graphics.FillPolygon(e.DesignerTheme.ForegroundBrush,
						new Point[]{
							new Point(center.X, center.Y),
							new Point(bounds.Right, bounds.Top),
							new Point(bounds.Right, bounds.Bottom)
						});
					break;
				case EventType.Link:
					break;
				case EventType.Multiple:
					break;
				case EventType.Termination:
					bounds.Inflate(-5, -5);
					using ( Pen crossPen = new Pen(Color.Red) ) {
						crossPen.Width = 2;
						e.Graphics.DrawLine(crossPen,
							bounds.Location, new Point(bounds.Right, bounds.Bottom));
						e.Graphics.DrawLine(crossPen,
							new Point(bounds.Left, bounds.Bottom), new Point(bounds.Right, bounds.Top));
					}
					break;
				default:
					break;
			}
		}

		protected override Size OnLayoutSize(ActivityDesignerLayoutEventArgs e)
		{
			return _size;
		}

		public override Size MinimumSize
		{
			get
			{
				return _size;
			}
		}
	}
	#endregion

	#region EventDrivenBranch
	[Designer(typeof(EventDrivenBranchDesigner))]
	public class EventDrivenBranch : HorizontalSequenceActivity
	{
		protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
		{
			return base.Execute(executionContext);
		}

		protected override ActivityExecutionStatus HandleFault(ActivityExecutionContext executionContext, Exception exception)
		{
			return base.HandleFault(executionContext, exception);
		}

		protected override ActivityExecutionStatus Cancel(ActivityExecutionContext executionContext)
		{
			return base.Cancel(executionContext);
		}
	}

	public class EventDrivenBranchDesigner : HorizontalSequenceDesigner
	{
		private List<Activity> _branchList = new List<Activity>();
		private List<Rectangle> _dropList = new List<Rectangle>();

		protected override Pen ConnectorPen
		{
			get { return new Pen(Color.DarkRed); }
		}

		public override void RemoveActivities(ReadOnlyCollection<Activity> activitiesToRemove)
		{
			base.RemoveActivities(activitiesToRemove);
		}

		protected override bool ShowFirstDropIndicator
		{
			get { return false; }
		}

		public override Size MinimumSize
		{
			get
			{
				return new Size(base.MinimumSize.Width, 32);
			}
		}

		public override bool CanExpandCollapse
		{
			get { return !( this.ParentDesigner is HorizontalEventHandlingScopeDesigner ); }
		}

		//protected IncomingEvent EventActivity
		//{
		//    get { return (IncomingEvent)( (EventDrivenBranch)this.Activity ).Activities[0]; }
		//}

		public override Image Image
		{
			get
			{
				return Resources.event_icon;
			}
			protected set
			{
				base.Image = value;
			}
		}

		protected override void OnPaint(ActivityDesignerPaintEventArgs e)
		{
			if ( CanExpandCollapse ) {
				ActivityDesignerPaint.DrawExpandButton(e.Graphics, ExpandButtonRectangle, Expanded, (CompositeDesignerTheme)e.DesignerTheme);
			}
			if ( Expanded ) {
				SizeF size = e.Graphics.MeasureString(Text, e.DesignerTheme.Font,
					Bounds.Width - 30, new StringFormat(StringFormatFlags.NoWrap));
				Point center = WorkflowHelpers.GetCenter(Bounds);
				_textRectangle = new Rectangle((int)( center.X - size.Width / 2 ), (int)Bounds.Top + 2,
					(int)Math.Ceiling(size.Width), (int)Math.Ceiling(size.Height));
				StringFormat format = new StringFormat();
				format.Alignment = StringAlignment.Center;
				format.Trimming = StringTrimming.EllipsisCharacter;

				e.Graphics.DrawString(Text, e.DesignerTheme.Font, e.DesignerTheme.ForegroundBrush, _textRectangle, format);

				base.OnPaint(e);
				if ( !CanExpandCollapse ) {
					e.Graphics.FillEllipse(Brushes.DarkRed, new Rectangle(Bounds.Right - 10, center.Y - 5, 10, 10));
				}
			} else {
				using ( Pen dashPen = new Pen(Color.SlateGray) ) {
					dashPen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
					ActivityDesignerPaint.DrawRoundedRectangle(e.Graphics, dashPen,
						Bounds, 10);
				}
			}
		}

		protected override Rectangle ExpandButtonRectangle
		{
			get
			{
				//return Rectangle.Empty;
				return new Rectangle(Location.X + 5, Location.Y + 1, 8, 8);
			}
		}

		public override void InsertActivities(HitTestInfo insertLocation, ReadOnlyCollection<Activity> activitiesToInsert)
		{
			// never insert before event
			//if (insertLocation.MapToIndex() == 0)
			//    return;

			base.InsertActivities(insertLocation, activitiesToInsert);
		}

		protected override void OnLayoutPosition(ActivityDesignerLayoutEventArgs e)
		{
			base.OnLayoutPosition(e);

			//if (Activity.QualifiedName != GetType().Name) {
			//    if (ContainedDesigners.Count == 0) {
			//        _branchList.Clear();
			//        _branchList.Add(new IncomingEvent());
			//        ReadOnlyCollection<Activity> roc = new ReadOnlyCollection<Activity>(_branchList);
			//        base.InsertActivities(HitTest(Location), roc);
			//    }
			//}
		}

		protected override Size OnLayoutSize(ActivityDesignerLayoutEventArgs e)
		{
			if ( Expanded ) return base.OnLayoutSize(e) +
				new Size(0, this.ContainedDesigners.Count == 0 ? 70 : 35);
			else return MinimumSize;
		}

		protected override void OnDragDrop(ActivityDragEventArgs e)
		{
			base.OnDragDrop(e);
		}

		protected override void OnDragOver(ActivityDragEventArgs e)
		{
			base.OnDragOver(e);
		}

		protected override ReadOnlyCollection<Point> GetInnerConnections(DesignerEdges edges)
		{
			return base.GetInnerConnections(edges);
		}

		protected override bool ShowFirstConnector
		{
			get
			{
				return !( this.ParentDesigner is HorizontalEventHandlingScopeDesigner ) ||
						( this.ContainedDesigners.Count == 0 );
			}
		}

		protected override bool ShowLastConnector
		{
			get { return false; }
		}

		private Rectangle _textRectangle = Rectangle.Empty;

		protected override Rectangle TextRectangle
		{
			get { return _textRectangle; }
		}
	}
	#endregion

	[Serializable]
	public class EventBasedXOrActivityToolboxItem : ActivityToolboxItem
	{
		public EventBasedXOrActivityToolboxItem(Type type)
			: base(type)
		{
			this.DisplayName = "Event Based Gateway";
		}

		private EventBasedXOrActivityToolboxItem(SerializationInfo info, StreamingContext context)
		{
			Deserialize(info, context);
		}
	}

	public enum EventType
	{
		None = 0,
		Message = 1,
		Timer = 2,
		Rule = 3,
		Exception = 4,
		Cancellation = 5,
		Compensation = 6,
		Link = 7,
		Multiple = 8,
		Termination = 9
	}

	public enum TimerType
	{
		Duration = 0,
		Expiration = 1
	}

	public enum EventHandlingType
	{
		Listener,
		Scope
	}
}
